#include "gd32f30x.h"
#include "stdio.h"
#include "stdint.h"
#include "string.h"
#include "DevicesFlash.h"
#include "DevicesSPIFlash.h"
#include "disk_virtual.h"


/* 分区表 */
#define MBR_ADDR                    0x00000000
#define MBR_SIZE                    4096
#define MBR_BLOCK_NUM               (MBR_SIZE / DISK_VIRTUAL_BLOCK_SIZE)

#define Partition_1_ADDR            (MBR_ADDR + MBR_SIZE)
#define Partition_1_SIZE            (1024 * 1024 * 32)
#define Partition_1_BLOCK_NUM       (Partition_1_SIZE / DISK_VIRTUAL_BLOCK_SIZE)

#define Partition_2_ADDR            (Partition_1_ADDR + Partition_1_SIZE)
#define Partition_2_SIZE            (1024 * 1024 * 4)
#define Partition_2_BLOCK_NUM       (Partition_2_SIZE / DISK_VIRTUAL_BLOCK_SIZE)

#define Partition_3_ADDR            (Partition_2_ADDR + Partition_2_SIZE)
#define Partition_3_SIZE            (1024 * 1024 * 32)
#define Partition_3_BLOCK_NUM       (Partition_3_SIZE / DISK_VIRTUAL_BLOCK_SIZE)

#define Partition_4_ADDR            (Partition_3_ADDR + Partition_3_SIZE)
#define Partition_4_SIZE            (1024 * 1024 * 32)
#define Partition_4_BLOCK_NUM       (Partition_4_SIZE / DISK_VIRTUAL_BLOCK_SIZE)


/* FAT文件系统 */
#define DBR_CLUSTER_SIZE            4096
#define DBR_SIZE                    DBR_CLUSTER_SIZE
#define FAT_SIZE                    (Partition_1_SIZE / DBR_CLUSTER_SIZE * 2)    /* 至少需要：磁盘大小 / 簇 * 2 */
#define DIR_NUM                     512

#define DBR_ADDR                    0x00000000
#define FAT_ADDR                    (DBR_ADDR + DBR_SIZE)
#define DIR_ADDR                    (FAT_ADDR + FAT_SIZE)
#define FILE_CMD_ADDR               (DIR_ADDR + DIR_NUM * 32)           /* 一条目录信息占32Byte */
#define FILE_CMD_SIZE               DBR_CLUSTER_SIZE                    /* 一个文件最小占一个簇 */
#define FILE_DATA_ADDR              (FILE_CMD_ADDR + FILE_CMD_SIZE)  
#define FILE_DATA_SIZE              DBR_CLUSTER_SIZE                    /* 一个文件最小占一个簇 */
#define FILE_FLASH_ADDR             (FILE_DATA_ADDR + FILE_DATA_SIZE)  
#define FILE_FLASH_SIZE             (1024 * 1024 * 1)
#define FILE_SPI_FLASH_ADDR         (FILE_FLASH_ADDR + FILE_FLASH_SIZE)  
#define FILE_SPI_FLASH_SIZE         (1024 * 1024 * 4)


const static uint8_t MBR[] = {
    /* 分区1信息 */
    /* 0x80：活动分区、0x00非活动分区 */
    [0x1BE] = 0x00,
    /* 使用LBA寻址，所以此处设置为0xFFFFFF */
    [0x1BF] = 0xFE, 0xFF, 0xFF,
    /* 0x06：FAT16、0x01：FAT32 */
    [0x1C2] = 0x06,
    /* 使用LBA寻址，所以此处设置为0xFFFFFF */
    [0x1C3] = 0xFE, 0xFF, 0xFF,
    /* LBA寻址 */
    [0x1C6] = (uint8_t) (Partition_1_ADDR / DISK_VIRTUAL_BLOCK_SIZE),
              (uint8_t)((Partition_1_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 8),
              (uint8_t)((Partition_1_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 16),
              (uint8_t)((Partition_1_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 24),
    [0x1CA] = (uint8_t)Partition_1_BLOCK_NUM,  (uint8_t)(Partition_1_BLOCK_NUM >> 8),  (uint8_t)(Partition_1_BLOCK_NUM >> 16),  (uint8_t)(Partition_1_BLOCK_NUM >> 24),

//    /* 分区2地址信息 */
//    /* 0x80：活动分区、0x00非活动分区 */
//    [0x1CE] = 0x00,
//    /* 使用LBA寻址，所以此处设置为0xFFFFFE */
//    [0x1CF] = 0xFE, 0xFF, 0xFF,
//    /* 0x06：FAT16、0x01：FAT32 */
//    [0x1D2] = 0x06,
//    /* 使用LBA寻址，所以此处设置为0xFFFFFE */
//    [0x1D3] = 0xFE, 0xFF, 0xFF,
//    /* LBA寻址 */
//    [0x1D6] = (uint8_t) (Partition_2_ADDR / DISK_VIRTUAL_BLOCK_SIZE),
//              (uint8_t)((Partition_2_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 8),
//              (uint8_t)((Partition_2_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 16),
//              (uint8_t)((Partition_2_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 24),
//    [0x1DA] = (uint8_t)Partition_2_BLOCK_NUM,  (uint8_t)(Partition_2_BLOCK_NUM >> 8),  (uint8_t)(Partition_2_BLOCK_NUM >> 16),  (uint8_t)(Partition_2_BLOCK_NUM >> 24),
//    
//    /* 分区3地址信息 */
//    /* 0x80：活动分区、0x00非活动分区 */
//    [0x1DE] = 0x00,
//    /* 使用LBA寻址，所以此处设置为0xFFFFFE */
//    [0x1DF] = 0xFE, 0xFF, 0xFF,
//    /* 0x06：FAT16、0x01：FAT32 */
//    [0x1E2] = 0x06,
//    /* 使用LBA寻址，所以此处设置为0xFFFFFE */
//    [0x1E3] = 0xFE, 0xFF, 0xFF,
//    /* LBA寻址 */
//    [0x1E6] = (uint8_t) (Partition_3_ADDR / DISK_VIRTUAL_BLOCK_SIZE),
//              (uint8_t)((Partition_3_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 8),
//              (uint8_t)((Partition_3_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 16),
//              (uint8_t)((Partition_3_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 24),
//    [0x1EA] = (uint8_t)Partition_3_BLOCK_NUM,  (uint8_t)(Partition_3_BLOCK_NUM >> 8),  (uint8_t)(Partition_3_BLOCK_NUM >> 16),  (uint8_t)(Partition_3_BLOCK_NUM >> 24),

//    /* 分区4地址信息 */
//    /* 0x80：活动分区、0x00非活动分区 */
//    [0x1EE] = 0x00,
//    /* 使用LBA寻址，所以此处设置为0xFFFFFE */
//    [0x1EF] = 0xFE, 0xFF, 0xFF,
//    /* 0x06：FAT16、0x01：FAT32 */
//    [0x1F2] = 0x06,
//    /* 使用LBA寻址，所以此处设置为0xFFFFFE */
//    [0x1F3] = 0xFE, 0xFF, 0xFF,
//    /* LBA寻址 */
//    [0x1F6] = (uint8_t) (Partition_4_ADDR / DISK_VIRTUAL_BLOCK_SIZE),
//              (uint8_t)((Partition_4_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 8),
//              (uint8_t)((Partition_4_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 16),
//              (uint8_t)((Partition_4_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 24),
//    [0x1FA] = (uint8_t)Partition_4_BLOCK_NUM,  (uint8_t)(Partition_4_BLOCK_NUM >> 8),  (uint8_t)(Partition_4_BLOCK_NUM >> 16),  (uint8_t)(Partition_4_BLOCK_NUM >> 24),

    [510] = 0x55, [511] = 0xAA,
};

const static uint8_t DBR1[] = {
    /* JUMP 指令 */
    0xEB, 0x3C, 0x90,
    /* OEM 厂商标志 */
    [0x03] = 'M',  'S',  'D',  'O',  'S',  '5',  '.',  '0',
    /* 扇区字节数 */
    [0x0B] = (uint8_t)DISK_VIRTUAL_BLOCK_SIZE, (uint8_t)(DISK_VIRTUAL_BLOCK_SIZE >> 8),
    /* 每簇扇区数 */
    [0x0D] = DBR_CLUSTER_SIZE / DISK_VIRTUAL_BLOCK_SIZE,
    /* 保留扇区数（FAT之前的扇区数，包含DBR） */
    [0x0E] = (uint8_t)(DBR_SIZE / DISK_VIRTUAL_BLOCK_SIZE), (uint8_t)((DBR_SIZE / DISK_VIRTUAL_BLOCK_SIZE) >> 8),
    /* FAT个数 */
    [0x10] = 0x01,
    /* 根目录项数（一般为512） */
    [0x11] = (uint8_t)DIR_NUM, (uint8_t)(DIR_NUM >> 8),
    /* 小扇区数（为0时，由后面的大扇区决定） */
    [0x13] = 0x00, 0x00,
    /* 0xF8：硬盘、0xF0：软盘 */
    [0x15] = 0xF8,
    /* 每个FAT所占扇区数 */
    [0x16] = (uint8_t)(FAT_SIZE / DISK_VIRTUAL_BLOCK_SIZE), (uint8_t)((FAT_SIZE / DISK_VIRTUAL_BLOCK_SIZE) >> 8),
    /* 每磁道扇区数（sectors per trark） */
    [0x18] = 0x00, 0x00,
    /* 磁头数（number of head） */
    [0x1A] = 0x00, 0x00,
    /* 分区起始扇区地址 */
    [0x1C] = (uint8_t) (Partition_1_ADDR / DISK_VIRTUAL_BLOCK_SIZE),
             (uint8_t)((Partition_1_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 8),
             (uint8_t)((Partition_1_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 16),
             (uint8_t)((Partition_1_ADDR / DISK_VIRTUAL_BLOCK_SIZE) >> 24),
    /* 分区大小（大扇区数） */
    [0x20] = (uint8_t)Partition_1_BLOCK_NUM,  (uint8_t)(Partition_1_BLOCK_NUM >> 8),  (uint8_t)(Partition_1_BLOCK_NUM >> 16),  (uint8_t)(Partition_1_BLOCK_NUM >> 24),
    /* 0x80：硬盘、0x00：软盘 */
    [0x24] = 0x80,
    /* 保留 */
    [0x25] = 0x00,
    /* 0x28、或者0x29 */
    [0x26] = 0x29,
    /* 4Byte 随机数 */
    [0x27] =0x35, 0x39, 0x36, 0x33,
    [0x2B] = 'H',  'o',  'n',  'r',  'u',  'n',  ' ',  ' ',  ' ',  ' ',  ' ',
    [0x36] = 'F',  'A',  'T',  '1',  '6',  ' ',  ' ',  ' ',

    /* 0x3E - 0x1FD: Bootstrap code */
    [510] = 0x55, [511] = 0xAA,
};

const static uint8_t DIR[] = {
    'N',  'E',  'T',  'W',  'O',  'R',  'K',  ' ',  ' ',  ' ',  ' ',  0x08, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

    'C',  'M',  'D',  ' ',  ' ',  ' ',  ' ',  ' ',  'T',  'X',  'T',  0x20, 0x00, 0x00, 0x00, 0x00,
    0x6A, 0x1A, 0xBE, 0x58, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x1A, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00,

    'D',  'A',  'T',  'A',  ' ',  ' ',  ' ',  ' ',  'T',  'X',  'T',  0x20, 0x00, 0x00, 0x00, 0x00,
    0x6A, 0x1A, 0xBE, 0x58, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x1A, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00,

    'F',  'L',  'A',  'S',  'H',  ' ',  ' ',  ' ',  'B',  'I',  'N',  0x20, 0x00, 0x00, 0x00, 0x00,
    0x6A, 0x1A, 0xBE, 0x58, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x1A, 0x04, 0x00,
    /* 文件大小 */
    (uint8_t)(FILE_FLASH_SIZE), (uint8_t)(FILE_FLASH_SIZE >> 8), (uint8_t)(FILE_FLASH_SIZE >> 16), (uint8_t)(FILE_FLASH_SIZE >> 24),

    'S',  'P',  'I',  'F',  'L',  'A',  'S',  'H',  'B',  'I',  'N',  0x20, 0x00, 0x00, 0x00, 0x00,
    0x6A, 0x1A, 0xBE, 0x58, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x1A,
    /* 文件存储起始簇 */
    (uint8_t) (FILE_FLASH_SIZE / DBR_CLUSTER_SIZE + 4),
    (uint8_t)((FILE_FLASH_SIZE / DBR_CLUSTER_SIZE + 4) >> 8),
    /* 文件大小 */
    (uint8_t)(FILE_SPI_FLASH_SIZE), (uint8_t)(FILE_SPI_FLASH_SIZE >> 8), (uint8_t)(FILE_SPI_FLASH_SIZE >> 16), (uint8_t)(FILE_SPI_FLASH_SIZE >> 24),
};


/* 磁盘内文件列表 */
const uint32_t uiFileSizeList[] = {FILE_CMD_SIZE, FILE_DATA_SIZE, FILE_FLASH_SIZE, FILE_SPI_FLASH_SIZE};
/* FAT表的有效长度 */
#define FAT_LENGTH      iRoundUp((FILE_CMD_SIZE + FILE_DATA_SIZE + FILE_FLASH_SIZE + FILE_SPI_FLASH_SIZE) / DBR_CLUSTER_SIZE, 4096)
/* FAT表的内容 */
#define FLASH_FAT_ADDR  (FLASH_BASE_ADDR + 1024 * 128 - FAT_LENGTH)


/* 使 iValue 对 iBase 对齐（任意值） */
int32_t iRoundUp(int32_t iValue, int32_t iBase)
{
    if((iValue % iBase) == 0)
        return iValue;

    iValue += iBase - (iValue % iBase);

    return iValue;
}

/* 程序自动生成FAT */
int8_t cDiskFatDeInit(void)
{
    uint32_t uiAddr = FLASH_FAT_ADDR, uiFileNow = 0, uiFileSize = 0;
    uint16_t usFatBlockNow = 2, usFatValue = 0;

    usFatValue = 0xFFF8;
    cFlashWriteDatas(uiAddr,     &usFatValue, 2);
    usFatValue = 0xFFFF;
    cFlashWriteDatas(uiAddr + 2, &usFatValue, 2);

    for(uiFileNow = 0; uiFileNow < (sizeof(uiFileSizeList) / sizeof(uiFileSizeList[0])); ++uiFileNow)
    {
        for(uiFileSize = DBR_CLUSTER_SIZE; uiFileSize < uiFileSizeList[uiFileNow]; uiFileSize += DBR_CLUSTER_SIZE)
        {
            usFatValue = usFatBlockNow + 1;
            cFlashWriteDatas(uiAddr + (usFatBlockNow * 2), &usFatValue, 2);
            ++usFatBlockNow;
        }

        usFatValue = 0xFFFF;
        cFlashWriteDatas(uiAddr + (usFatBlockNow * 2), &usFatValue, 2);
        ++usFatBlockNow;
    }

    usFatValue = 0;
    for(; usFatBlockNow < (FAT_LENGTH / 2); ++usFatBlockNow)
    {
        cFlashWriteDatas(uiAddr + (usFatBlockNow * 2), &usFatValue, 2);
    }

    return 0;
}

int8_t cDiskFatCheck(void)
{
    uint32_t uiAddr = FLASH_FAT_ADDR, uiFileNow = 0, uiFileSize = 0;
    uint16_t usFatBlockNow = 2, usFatValue = 0;

    cFlashReadDatas(uiAddr,     &usFatValue, 2);
    if(usFatValue != 0xFFF8)
        return 1;

    cFlashReadDatas(uiAddr + 2, &usFatValue, 2);
    if(usFatValue != 0xFFFF)
        return 1;

    for(uiFileNow = 0; uiFileNow < (sizeof(uiFileSizeList) / sizeof(uiFileSizeList[0])); ++uiFileNow)
    {
        for(uiFileSize = DBR_CLUSTER_SIZE; uiFileSize < uiFileSizeList[uiFileNow]; uiFileSize += DBR_CLUSTER_SIZE)
        {
            cFlashReadDatas(uiAddr + (usFatBlockNow * 2), &usFatValue, 2);
            if(usFatValue != usFatBlockNow + 1)
                return 1;
    
            ++usFatBlockNow;
        }

        cFlashReadDatas(uiAddr + (usFatBlockNow * 2), &usFatValue, 2);
        if(usFatValue != 0xFFFF)
            return 1;

        ++usFatBlockNow;
    }

    for(; usFatBlockNow < (FAT_LENGTH / 2); ++usFatBlockNow)
    {
        cFlashReadDatas(uiAddr + (usFatBlockNow * 2), &usFatValue, 2);
        if(usFatValue != 0)
            return 1;
    }

    return 0;
}

int8_t cDiskVirtualInit(void)
{
    if(cDiskFatCheck() != 0)
    {
        cDiskFatDeInit();
    }

    return 0;
}

int8_t cDiskVirtualWrite(uint32_t uiAddress, const void *pvBuff, int32_t iLength)
{
    /* MBR */
    if(uiAddress < Partition_1_ADDR)
    {
    }
    /* Partition x */
    else
    {
        /* Partition 1 */
        if(uiAddress < Partition_2_ADDR)
        {
            uiAddress -= Partition_1_ADDR;

            /* CMD.BIN (size: 4096Byte) */
            if((FILE_CMD_ADDR <= uiAddress) && (uiAddress < (FILE_CMD_ADDR + FILE_CMD_SIZE)))
            {
                iLength = iLength < FILE_CMD_SIZE ? iLength : FILE_CMD_SIZE;
            }
            /* DATA.BIN (size: 4096Byte) */
            else if((FILE_DATA_ADDR <= uiAddress) && (uiAddress < (FILE_DATA_ADDR + FILE_DATA_SIZE)))
            {
                iLength = iLength < FILE_DATA_SIZE ? iLength : FILE_DATA_SIZE;
            }
        }
        /* Partition 2 */
        else if(uiAddress < Partition_3_ADDR)
        {
            uiAddress -= Partition_2_ADDR;
        }
    }

    return 0;
}

int8_t cDiskVirtualRead(uint32_t uiAddress, void *pvBuff, int32_t iLength)
{
    memset(pvBuff, 0, iLength);

    /* MBR */
    if(uiAddress < Partition_1_ADDR)
    {
        if(uiAddress < sizeof(MBR))
        {
            iLength = iLength < sizeof(MBR) ? iLength : sizeof(MBR);
            memcpy(pvBuff, MBR, iLength);
        }
    }
    /* Partition x */
    else
    {
        /* Partition 1 */
        if(uiAddress < Partition_2_ADDR)
        {
            uiAddress -= Partition_1_ADDR;

            /* DBR */
            if((DBR_ADDR <= uiAddress) && (uiAddress < (DBR_ADDR + sizeof(DBR1))))
            {
                iLength = iLength < sizeof(DBR1) ? iLength : sizeof(DBR1);
                memcpy(pvBuff, DBR1, iLength);
            }
            /* FAT */
            else if((FAT_ADDR <= uiAddress) && (uiAddress < (FAT_ADDR + FAT_LENGTH)))
            {
                uiAddress -= FAT_ADDR;
                iLength = iLength < (FAT_LENGTH - uiAddress) ? iLength : (FAT_LENGTH - uiAddress);

                memcpy(pvBuff, (void *)(FLASH_FAT_ADDR + uiAddress), iLength);
            }
            /* DIR */
            else if((DIR_ADDR <= uiAddress) && (uiAddress < (DIR_ADDR + sizeof(DIR))))
            {
                iLength = iLength < sizeof(DIR) ? iLength : sizeof(DIR);
                memcpy(pvBuff, DIR, iLength);
            }
            /* CMD.TXT (size: 4096Byte) */
            else if((FILE_CMD_ADDR <= uiAddress) && (uiAddress < (FILE_CMD_ADDR + FILE_CMD_SIZE)))
            {
                iLength = iLength < FILE_CMD_SIZE ? iLength : FILE_CMD_SIZE;
            }
            /* DATA.TXT (size: 4096Byte) */
            else if((FILE_DATA_ADDR <= uiAddress) && (uiAddress < (FILE_DATA_ADDR + FILE_DATA_SIZE)))
            {
                iLength = iLength < FILE_DATA_SIZE ? iLength : FILE_DATA_SIZE;
            }
            /* FLASH.BIN (size: FILE_FLASH_SIZE Byte) */
            else if((FILE_FLASH_ADDR <= uiAddress) && (uiAddress < (FILE_FLASH_ADDR + FILE_FLASH_SIZE)))
            {
                uiAddress -= FILE_FLASH_ADDR;

                memcpy(pvBuff, (uint32_t *)(0x08000000U + uiAddress), iLength);
            }
            /* SPIFLASH.BIN (size: FILE_SPI_FLASH_SIZE Byte) */
            else if((FILE_SPI_FLASH_ADDR <= uiAddress) && (uiAddress < (FILE_SPI_FLASH_ADDR + FILE_SPI_FLASH_SIZE)))
            {
                uiAddress -= FILE_SPI_FLASH_ADDR;

                cSPIFlashReadDatas(uiAddress, pvBuff, iLength);
            }
        }
        /* Partition 2 */
        else if(uiAddress < Partition_3_ADDR)
        {
            uiAddress -= Partition_2_ADDR;
        }
        /* Partition 3 */
        else if(uiAddress < Partition_4_ADDR)
        {
            uiAddress -= Partition_3_ADDR;
        }
        /* Partition 4 */
        else
        {
            uiAddress -= Partition_4_ADDR;
        }
    }

    return 0;
}
